<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <script>
    function fetch() {
      return new Promise(resolve => {
        console.log('fetch');
        setTimeout(() => resolve('成功了'), 2000)
      })
    }
    /*
      原始写法
        问题： 函数都不是纯函数了、链式调用写法复杂
    */
    // async function main() {
    //   return await fetch()
    // }
    // async function walk() {
    //   return await main()
    // }
    // async function run() {
    //   return await walk()
    // }
    // async function init() {
    //   let result = await run()
    //   console.log('原始', result);
    // }
    // init()
    console.log('------------------------------');

    /**
     * 改写成一下方式也可以达到同样的效果:
     * 思想：main > walk > run > init
     *       
    */

    function m1() {
      return fetch()
    }
    function m2() {
      let result = m1()
      console.log('m2');
      return 
    }
    function m3() {
      let result = m2()
      console.log('m3');
      return 
    }
    function main() {
      let result = m3()
      console.log(1111, result);
    }
    /**
     * 思路： 第一次执行：run1 > main > m3 > m2 > m1 > fetch
     *  重写fetch，使用缓存缓存请求的结果，第二次fetch时可直接取缓存值
     *  在newfetch中同步抛出一个错误，错误返回fetch的请求Promise对象,抛出错误后main m3 m2后面的代码都不会执行了
     *  run1中捕获到错误，拿到请求对象，请求完成之后重新调用run1
     * */ 


    // 临时将fetch函数重写，暂时只考虑只有一个请求
    function run1(func) {
      // 改变fetch
      const oldFetch = window.fetch

      // 设置缓存
      const cache = {
        status: 'pedding',
        value: null
      }
      
      function newFetch(...arg) {
        if(cache.status === 'fulfilled') {
          return cache.value
        }else if(cache.status === 'rejected') {
          throw cache.value
        }
        const p = oldFetch(...arg).then((val) => {
            cache.status = 'fulfilled'
            cache.value = val
          }).catch((err) => {
            cache.status = 'rejected'
            cache.value = err
          })
        throw p
      }
      window.fetch = newFetch

      // 执行func
      try {
        func()
      } catch (error) {
        if(error instanceof Promise) {
          error.finally(() => {
            // 第二次运行func时，fetch已经被改回去了(为什么需要改回去呢，反之catch里面的代码不执行)，所以这里需要再改一次
            window.fetch = newFetch
            func()
            window.fetch = oldFetch
          })
        }
      }   

      // 改回fetch
      window.fetch = oldFetch
    }
    run1(main)
  </script>
</body>

</html>